#ifndef __JUNCTION_FINDER__H
#define __JUNCTION_FINDER__H
#include <vector>
#include "types.h"
#include "Point.h"
#include "Image.h"
#include "ViewWithThreshold.h"
#include "RunFinder.h"

class JunctionFinder : protected ViewBW {
public:
	// parameters for finding junctions of a vein (used in GetVeinInfo)
	struct Criteria { 
		Criteria() : m_nMinSize(0), m_nMaxJunctionDistance(0), m_bPathAllowedToEnd(false) {}
		
		/* for normal vein finding, this is the minimal size on which the junction's
		 * nvbr must go on
		 * example: for the following drawing:
		 *
		 *                    **
		 *                   **
		 *                  **
		 *                 **
		 *                  **
		 *                   **
		 *                    **
		 * the size on which the junction's nvbr goes on is 3
		 * default value: 0 (what result does this cause?)
		 */ 
		S32 m_nMinSize;
		
		/* for normal vein finding, this is the maximal allowed distance between 
		 * the junctions' top branch's first row bottomost pixel
		 * to junctions' bottom branch's first row topmost pixel
		 *
		 *                 **X*
		 *                 **
		 *                 **
		 *                 **X
		 *                    *
		 * (for this example the distance is 3 between the two Xs)
		 * this one is used to differentiate from 90 degree junctions
		 * in most cases, it can be set to the same value as m_nNon90VeinMaxThickness
		 * if we are greater than this distance, we assume we got a 90 degree junction
		 * if this number is not bigger than zero, it is ignored.
		 * default value: 0 (means ignore this)
		 */
		S32 m_nMaxJunctionDistance; 
		
		/* in some junctions, we get something which looks like this
		 *
		 *                 **X****
		 *                 **
		 *                 **
		 *                 **X
		 *                    ********
		 * if m_nMinSize is more than 5 we have a problem,
		 * is this a junction or not?
		 * if this value is true, then it is, otherwise, it is not
		 * default value: false
		 */
		bool m_bPathAllowedToEnd;
	};
	
	struct Info {
		
		struct Node {
			std::vector< Point <S32> > m_vecPtDraw;
			S32 m_nViewStateIndex; // negative if unknown
			// this whole "blocked" thing should be renamed to a saner name
			// something like "joins" from above = "blocks" from below
			class Blocked {
			public:
				Blocked() : m_nData(0) {}
				bool FromAbove() const { return m_nData & 1; }
				bool FromBelow() const { return m_nData & 2; }
				bool FromBoth() const { return m_nData & 3; }
				bool FromNone() const { return !m_nData; }
				void SetFromAbove() { m_nData |= 1; }
				void SetFromBelow() { m_nData |= 2; }
			private:
				S32 m_nData;
			};
			std::vector< Blocked > m_vecBlocked;
			std::vector< Run > m_vecRunsEdge;
			std::vector< S32 > m_vecEdgeCol;
		};
		
		struct Node90 {
			std::vector< Point <S32> > m_vecPtDraw;
			S32 m_nViewStateIndex; // negative if unknown
		};
		/* junctions with a small angle */
		std::vector< Node > m_vecJunction; 
		std::vector< Node > m_vecJunctionOppositeAngle;
		/* junctions with a large (near 90) angle */
		std::vector< Node90 > m_vecJunction90;
		std::vector< Node90 > m_vecJunction90OppositeAngle;	
	};
	
	JunctionFinder(const Image *pImage) : ViewBW(pImage, 0, 0, 0) {}
	
	const View *GetView() const { return View::GetThis(); }
	void GetJunctions(const std::vector < ViewState > *pVecStates1, 
	                  const std::vector < ViewState > *pVecStates2, 
	                  const Criteria *pJc, Info *pJi);
	void GetJunctionInSingleDirection(const ViewState *pState, 
                                          const Criteria *pJc,
                                          std::vector < Info::Node > *pJunctionVec,
                                          std::vector < Info::Node90 > *p90JunctionVec); 
	// this routine is needed for us to be able to work correctly with RunFinder
	// (compile-time polymorphism)
	U8 GetPix(F64 nRow, F64 nCol) const { return ViewBW::GetPix(nRow, nCol); } 
private:
	class Found {
	public:
		Found() : m_nData(0) {}
		void SetFound()           { m_nData |= 1; }
		void SetFound90()         { m_nData |= 2; }
		void SetFoundOpposite()   { m_nData |= 4; }
		void SetFound90Opposite() { m_nData |= 8; }
		S8 IsFound()           { return m_nData & 1; }
		S8 IsFound90()         { return m_nData & 2; }
		S8 IsFoundOpposite()   { return m_nData & 4; }
		S8 IsFound90Opposite() { return m_nData & 8; }
		S8 IsFoundAnything() { return m_nData; }
	private:
		S8 m_nData;
	};
	Found AddIfJunctionPoint(Info *pInfo, const Criteria *pCriteria);
	Found AddIfJunctionPointInSingleDirection(std::vector< Info::Node > *pJunctionVec,
	                                          std::vector< Info::Node90 > *pJunction90Vec,
	                                          const Criteria *pCriteria);
	void UpdateStateIndex(Info *pInfo, Found f, S32 nState);
};
#endif//__JUNCTION_FINDER__H
